local ngx = ngx
local redis = require("resty.redis")
local string = string
local cjson = require("cjson")



local _M = {}
local _mt = { __index = _M }


local redis_config = {
    host = "127.0.0.1",
    port = 6379,
    timeout = 1000,
    password = "",
    db = 0,
    keepalive = 60000, -- 连接池保持时间
    pool_size = 100,   -- 连接池大小
}

local function connect_redis()
    local red = redis:new()
    red:set_timeout(redis_config.timeout)

    local ok,err = red:connect(redis_config.host,redis_config.port)
    if not ok then
        ngx.log(ngx.ERR,"无法连接到redis, err : " ,err)
        return nil,err
    end

    -- 校验密码
    if redis_config.password and redis_config.password ~= "" then
        local ok,err = red:auth(redis_config.password)
        if not ok then
             ngx.log(ngx.ERR,"redis 密码错误, err : " ,err)
             red:close()
             return nil,err
        end
    end

    -- select db 
    if redis_config.db and redis_config.db ~= 0 then
        local ok,err = red:select(redis_config.db)
          if not ok then
             ngx.log(ngx.ERR,"redis select db错误, err : " ,err)
             red:close()
             return nil,err
        end
    end

    return red
end


-- 连接池，当无空闲连接时会创建新的连接，然后操作完成后，这个连接会放在连接池
-- 下次操作时如果连接池里面有空闲的连接，会服用该连接，重复这个操作
-- 后续请求时，OpenResty 的 resty.redis 库会优先从连接池中获取空闲连接（而非新建）。
local function keepalive_redis(red)
    if not red then
        return
    end


    local ok,err = red:set_keepalive(redis_config.keepalive,redis_config.pool_size)
    if not ok then
        ngx.log(ngx.ERR,"Redis 连接放回连接池失败,err: ",err) 
        red:close()
    end
end


function _M.set(key,value,expire)
    if not key or key == "" then
        return nil,"缓存键名不能为空"
    end
    
    local red,err = connect_redis()
    if not red then
        return nil,err 
    end

    local cache_value = value
    if type(value) == "table" then
        cache_value,err = cjson.encode(value)
        if not cache_value then
            keepalive_redis(red)
            return nil,"序列化数据失败： " .. (err or "")
        end
    end

    -- 设置缓存
    local ok,err
    if expire and expire > 0 then
        ok,err = red:setex(key,expire,cache_value)
    else
        ok,err = red:set(key,cache_value)
    end

    keepalive_redis(red)

    if not ok then
        return nil,"设置缓存失败: " .. (err or "")
    end

    return true
end


function _M.get(key,is_json)
    if not key or key == "" then
        return nil,"缓存键名不能为空"
    end

    local red,err = connect_redis()
      if not red then
        return nil,err 
    end

    local value ,err = red:get(key)
    keepalive_redis(red)

    if err then
        return nil,"获取缓存失败: " .. (err or "")
    end


    if not value then
        return  nil,nil
    end

    -- 处理json序列化
    local is_json_flag = is_json
    if is_json == nil then
        -- 自动判断是否为json字符串
        is_json_flag = (string.sub(value,1,1) == "{" or string.sub(value,1,1) == "[")
    end

    if is_json_flag then
        local data,err = cjson.decode(value)
        if err then
            return nil,"解析缓存失败: " .. (err or "")
        end

        return data,nil 
    end

    return value,nil
end

-- 删除缓存
function _M.delete(key)
    if not key or key == "" then
        return nil,"缓存键名不能为空"
    end    

   
    local red,err = connect_redis()
      if not red then
        return nil,err 
    end

    local ok,err = red:del(key)
    keepalive_redis(red)
    if err then
        return nil,"删除缓存失败: " .. (err or "")        
    end

    return ok > 0,nil 
end

function _M.exists(key)
     if not key or key == "" then
        return nil,"缓存键名不能为空"
    end    

     local red,err = connect_redis()
      if not red then
        return nil,err 
    end

    local exists,err = red:exists(key)
    keepalive_redis(red)
    if err then
        return nil,"检查缓存是否存在失败: " .. (err or "")        
    end

    return exists == 1,nil 
end

function _M.expire(key,seconds)
    if not key or key == "" then
        return nil,"缓存键名不能为空"
    end    

    if not seconds or seconds < 0 then
        return nil,"过期时间必须大于0"
    end

     local red,err = connect_redis()
      if not red then
        return nil,err 
    end

    local ok,err = red:expire(key,seconds)
    keepalive_redis(red)
    if err then
        return nil,"设置缓存过期时间失败: " .. (err or "")        
    end

    return ok == 1,nil 

end




return _M

